/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Graphics;

namespace Cobalt.Content.Pipeline.Quake.Shaders
{
 	public class Shader : ContentItem
	{
		public Shader( string name )
		{
			if( name == null )
				throw new ArgumentNullException( "name" );

			this.Name = name;
		}

		private bool isExplicitelyDefined;
		public bool IsExplicitelyDefined { get { return isExplicitelyDefined; } set { isExplicitelyDefined = value; } }

		private SkyParams sky;
		public SkyParams Sky { get { return sky; } set { sky = value; } }

		private ShaderSort sort = ShaderSort.Default;
		public ShaderSort Sort { get { return sort; } set { sort = value; } }

		private CullMode cull = CullMode.CullCounterClockwiseFace;
		public CullMode Cull { get { return cull; } set { cull = value; } }

		private int polygonOffset;
		public int PolygonOffset { get { return polygonOffset; } set { polygonOffset = value; } }

		private List<VertexDeformation> deformations = new List<VertexDeformation>();
		public List<VertexDeformation> Deformations { get { return deformations; } }

		private List<ShaderStage> stages = new List<ShaderStage>();
		public List<ShaderStage> Stages { get { return stages; } }

		public Shader Clone()
		{
			Shader ret = new Shader( Name );
			ret.Identity = Identity;

			ret.isExplicitelyDefined = isExplicitelyDefined;
			ret.sky = sky != null ? sky.Clone() : null;
			ret.sort = sort;
			ret.cull = cull;
			ret.polygonOffset = polygonOffset;
			foreach( var deform in deformations )
				ret.deformations.Add( deform.Clone() );
			foreach( var stage in stages )
				ret.stages.Add( stage.Clone() );

			return ret;
		}

		public static Shader DefaultLightmappedShader
		{
			get
			{
				Shader shader;
				ShaderStage stage;
				TextureBundle bundle;

				shader = new Shader( "@default_lightmapped" );
				shader.IsExplicitelyDefined = false;

				stage = new ShaderStage();

				bundle = new TextureBundle();
				bundle.Images.Add( TextureBundle.Lightmap );
				bundle.TexCoordGen = new LightmapTexCoordGen();
				bundle.TextureBlend.ColorOperation = TextureBlendOp.Replace;
				bundle.TextureBlend.AlphaOperation = TextureBlendOp.Replace;

				stage.Bundles.Add( bundle );

				bundle = new TextureBundle();
				bundle.Images.Add( TextureBundle.TextureImage );
				bundle.TexCoordGen = new TextureTexCoordGen();

				stage.Bundles.Add( bundle );

				shader.Stages.Add( stage );

				return shader;
			}
		}
		public static Shader DefaultVertexLitShader
		{
			get
			{
				Shader shader;
				ShaderStage stage;
				TextureBundle bundle;

				shader = new Shader( "$default_vertex_lit" );

				stage = new ShaderStage();

				stage.RgbGen = new VertexColorGen();

				bundle = new TextureBundle();
				bundle.Images.Add( TextureBundle.TextureImage );
				bundle.TexCoordGen = new TextureTexCoordGen();

				stage.Bundles.Add( bundle );

				shader.Stages.Add( stage );

				return shader;
			}
		}
	}

	public enum ShaderSort
	{
		Default,

		Portal,
		Environment,
		Opaque,

		Decal,
		SeeThrough,

		Banner,

		Flare,

		Fog,

		Underwater,

		Blend0,
		Blend1,
		Blend2,
		Blend3,
		Blend4,
		Blend5,
		Blend6,

		StencilShadow,
		AlmostNearest,
		Nearest,
	}

	public abstract class VertexDeformation
	{
		internal VertexDeformation() { }

		public abstract VertexDeformation Clone();
	}

	public sealed class BulgeDeformation : VertexDeformation
	{
		private float width;
		public float Width { get { return width; } set { width = value; } }

		private float height;
		public float Height { get { return height; } set { height = value; } }

		private float speed;
		public float Speed { get { return speed; } set { speed = value; } }

		public override VertexDeformation Clone()
		{
			BulgeDeformation ret = new BulgeDeformation();

			ret.width = width;
			ret.height = height;
			ret.speed = speed;

			return ret;
		}
	}

	public sealed class WaveDeformation : VertexDeformation
	{
		private float spread;
		public float Spread { get { return spread; } set { spread = value; } }

		private Waveform waveform;
		public Waveform Waveform { get { return waveform; } set { waveform = value; } }

		public override VertexDeformation Clone()
		{
			WaveDeformation ret = new WaveDeformation();

			ret.spread = spread;
			ret.waveform = waveform;

			return ret;
		}
	}

	public sealed class NormalDeformation : VertexDeformation
	{
		private float amplitude;
		public float Amplitude { get { return amplitude; } set { amplitude = value; } }

		private float frequency;
		public float Frequency { get { return frequency; } set { frequency = value; } }

		public override VertexDeformation Clone()
		{
			NormalDeformation ret = new NormalDeformation();

			ret.amplitude = amplitude;
			ret.frequency = frequency;

			return ret;
		}
	}

	public sealed class ExpandDeformation : VertexDeformation
	{
		private float amplitude;
		public float Amplitude { get { return amplitude; } set { amplitude = value; } }

		public override VertexDeformation Clone()
		{
			ExpandDeformation ret = new ExpandDeformation();

			ret.amplitude = amplitude;

			return ret;
		}
	}

	public class ShaderStage
	{
		private List<TextureBundle> bundles = new List<TextureBundle>();
		public List<TextureBundle> Bundles { get { return bundles; } }

		private ColorGen rgbGen = new ConstantColorGen();
		public ColorGen RgbGen { get { return rgbGen; } set { rgbGen = value; } }

		private AlphaGen aGen = new ConstantAlphaGen();
		public AlphaGen AlphaGen { get { return aGen; } set { aGen = value; } }

		private DepthTest depthTest = new DepthTest();
		public DepthTest DepthTest { get { return depthTest; } }		

		private AlphaTest alphaTest = new AlphaTest();
		public AlphaTest AlphaTest { get { return alphaTest; } }

		private AlphaBlend blend = new AlphaBlend();
		public AlphaBlend AlphaBlend { get { return blend; } }

		public ShaderStage Clone()
		{
			ShaderStage ret = new ShaderStage();

			foreach( var bundle in bundles )
				ret.bundles.Add( bundle.Clone() );

			ret.rgbGen = rgbGen.Clone();
			ret.aGen = aGen.Clone();
			ret.depthTest = depthTest.Clone();
			ret.alphaTest = alphaTest.Clone();
			ret.blend = blend.Clone();

			return ret;
		}
	}

	public class DepthTest
	{
		private bool enable = true;
		public bool Enable { get { return enable; } set { enable = value; } }

		private CompareFunction function = CompareFunction.LessEqual;
		public CompareFunction Function { get { return function; } set { function = value; } }

		private bool depthWrite = true;
		public bool WriteEnable { get { return depthWrite; } set { depthWrite = value; } }

		public DepthTest Clone()
		{
			DepthTest ret = new DepthTest();

			ret.enable = enable;
			ret.function = function;
			ret.depthWrite = depthWrite;

			return ret;
		}
	}

	public class AlphaTest
	{
		private bool enable = false;
		public bool Enable { get { return enable; } set { enable = value; } }

		private CompareFunction function;
		public CompareFunction Function { get { return function; } set { function = value; } }

		private byte reference;
		public byte Reference { get { return reference; } set { reference = value; } }

		public AlphaTest Clone()
		{
			AlphaTest ret = new AlphaTest();

			ret.enable = enable;
			ret.function = function;
			ret.reference = reference;

			return ret;
		}
	}

	public class AlphaBlend
	{
		private bool enable = false;
		public bool Enable { get { return enable; } set { enable = value; } }

		private Blend sourceBlend = Blend.One;
		public Blend SourceBlend { get { return sourceBlend; } set { sourceBlend = value; } }

		private Blend destBlend = Blend.Zero;
		public Blend DestBlend { get { return destBlend; } set { destBlend = value; } }

		public AlphaBlend Clone()
		{
			AlphaBlend ret = new AlphaBlend();

			ret.enable = enable;
			ret.sourceBlend = sourceBlend;
			ret.destBlend = destBlend;

			return ret;
		}
	}

	public abstract class ColorGen
	{
		internal ColorGen() { }

		public abstract ColorGen Clone();
	}

	public sealed class ConstantColorGen : ColorGen
	{
		private Color color = Color.White;
		public Color Color { get { return color; } set { color = value; } }

		public override ColorGen Clone()
		{
			ConstantColorGen ret = new ConstantColorGen();

			ret.color = color;

			return ret;
		}

		public override int GetHashCode() { return color.R | color.G << 8 | color.B << 16; }
		public override bool Equals( object obj )
		{
			ConstantColorGen other = obj as ConstantColorGen;
			if( other == null )
				return false;

			return other.color.R == color.R &&
				other.color.G == color.G &&
				other.color.B == color.B;
		}
	}

	public sealed class WaveColorGen : ColorGen
	{
		private Waveform waveform;
		public Waveform Waveform { get { return waveform; } set { waveform = value; } }

		public override ColorGen Clone()
		{
			WaveColorGen ret = new WaveColorGen();

			ret.waveform = waveform;

			return ret;
		}

		public override int GetHashCode() { return waveform.GetHashCode(); }
		public override bool Equals( object obj )
		{
			WaveColorGen other = obj as WaveColorGen;
			return other != null && other.waveform == waveform;
		}
	}

	public sealed class VertexColorGen : ColorGen
	{
		private bool invertValue = false;
		public bool InvertValue { get { return invertValue; } set { invertValue = value; } }

		public override ColorGen Clone()
		{
			VertexColorGen ret = new VertexColorGen();

			ret.invertValue = invertValue;

			return ret;
		}

		public override int GetHashCode() { return invertValue.GetHashCode() + 20; }
		public override bool Equals( object obj )
		{
			VertexColorGen other = obj as VertexColorGen;
			return other != null && other.invertValue == invertValue;
		}
	}

	public abstract class AlphaGen
	{
		internal AlphaGen() { }

		public abstract AlphaGen Clone();
	}

	public sealed class ConstantAlphaGen : AlphaGen
	{
		private byte alpha = 0xFF;
		public Byte Alpha { get { return alpha; } set { alpha = value; } }

		public override AlphaGen Clone()
		{
			ConstantAlphaGen ret = new ConstantAlphaGen();

			ret.alpha = alpha;

			return ret;
		}

		public override int GetHashCode() { return alpha.GetHashCode(); }
		public override bool Equals( object obj )
		{
			ConstantAlphaGen other = obj as ConstantAlphaGen;
			return other != null && other.alpha == alpha;
		}
	}

	public sealed class WaveAlphaGen : AlphaGen
	{
		private Waveform waveform;
		public Waveform Waveform { get { return waveform; } set { waveform = value; } }

		public override AlphaGen Clone()
		{
			WaveAlphaGen ret = new WaveAlphaGen();

			ret.waveform = waveform;

			return ret;
		}

		public override int GetHashCode() { return waveform.GetHashCode(); }
		public override bool Equals( object obj )
		{
			WaveAlphaGen other = obj as WaveAlphaGen;
			return other != null && other.waveform == waveform;
		}
	}

	public sealed class VertexAlphaGen : AlphaGen
	{
		private bool invertValue;
		public bool InvertValue { get { return invertValue; } set { invertValue = value; } }

		public override AlphaGen Clone()
		{
			VertexAlphaGen ret = new VertexAlphaGen();

			ret.invertValue = invertValue;

			return ret;
		}

		public override int GetHashCode() { return invertValue.GetHashCode() + 10; }
		public override bool Equals( object obj )
		{
			VertexAlphaGen other = obj as VertexAlphaGen;
			return other != null && other.invertValue == invertValue;
		}
	}

	public class TextureBundle
	{
		public const string TextureImage = "$diffuseimage";
		public const string WhiteImage = "$whiteimage";
		public const string Lightmap = "$lightmap";

		public static bool IsImagePlaceholder( string imageName )
		{
			return imageName == TextureImage ||
				imageName == WhiteImage ||
				imageName == Lightmap;
		}

		private List<string> images = new List<string>();
		public List<string> Images { get { return images; } }

		private float imageAnimationSpeed = 0;
		public float ImageAnimationSpeed { get { return imageAnimationSpeed; } set { imageAnimationSpeed = value; } }

		private TexCoordGen texGen = new TextureTexCoordGen();
		public TexCoordGen TexCoordGen { get { return texGen; } set { texGen = value; } }

		private List<TexCoordMod> mods = new List<TexCoordMod>();
		public List<TexCoordMod> TexCoordMods { get { return mods; } }

		private TextureAddressMode addressMode = TextureAddressMode.Wrap;
		public TextureAddressMode AddressMode { get { return addressMode; } set { addressMode = value; } }

		private TextureBlend blend = new TextureBlend();
		public TextureBlend TextureBlend { get { return blend; } }

		public TextureBundle Clone()
		{
			TextureBundle ret = new TextureBundle();

			ret.images.AddRange( images );
			ret.imageAnimationSpeed = imageAnimationSpeed;
			ret.texGen = texGen.Clone();
			foreach( var mod in mods )
				ret.mods.Add( mod.Clone() );
			ret.addressMode = addressMode;
			ret.blend = blend.Clone();

			return ret;
		}
	}

	public class SkyTextureBundle : TextureBundle
	{
		public SkyTextureBundle( string imageName )
		{
			AddressMode = TextureAddressMode.Clamp;
			Images.Add( imageName );
			TexCoordGen = new SkyTexCoordGen();
		}

		public sealed class SkyTexCoordGen : TexCoordGen
		{
			public override TexCoordGen Clone()
			{
				return this;
			}
		}
	}

	public class BumpTextureBundle : TextureBundle
	{
		public BumpTextureBundle( string imageName )
		{
			AddressMode = TextureAddressMode.Wrap;
			Images.Add( imageName );
			TexCoordGen = new BumpTexCoordGen();
		}

		public override int GetHashCode()
		{
			return Images[0].GetHashCode();
		}

		public override bool Equals( object obj )
		{
			BumpTextureBundle other = obj as BumpTextureBundle;
			return other != null && other.Images[0] == Images[0];
		}

		public sealed class BumpTexCoordGen : TexCoordGen
		{
			public override TexCoordGen Clone()
			{
				return this;
			}
		}
	}

	public abstract class TexCoordGen
	{
		internal TexCoordGen() { }
		public virtual int TexCoordDimensions { get { return 2; } }

		public abstract TexCoordGen Clone();
	}

	public sealed class TextureTexCoordGen : TexCoordGen
	{
		public override int GetHashCode() { return 1; }
		public override bool Equals( object obj ) { return obj is TextureTexCoordGen; }

		public override TexCoordGen Clone()
		{
			return this; //immutable
		}
	}

	public sealed class LightmapTexCoordGen : TexCoordGen
	{
		public override int GetHashCode() { return 2; }
		public override bool Equals( object obj ) { return obj is LightmapTexCoordGen; }

		public override TexCoordGen Clone()
		{
			return this; //immutable
		}
	}

	public sealed class EnvironmentMappedTexCoordGen : TexCoordGen
	{
		public override int GetHashCode() { return 3; }
		public override bool Equals( object obj ) { return obj is EnvironmentMappedTexCoordGen; }

		public override TexCoordGen Clone()
		{
			return this; //immutable
		}
	}

	public sealed class VectorTexCoordGen : TexCoordGen
	{
		private Vector3 vectorU;
		public Vector3 VectorU { get { return vectorU; } set { vectorU = value; } }

		private Vector3 vectorV;
		public Vector3 VectorV { get { return vectorV; } set { vectorV = value; } }

		public override TexCoordGen Clone()
		{
			VectorTexCoordGen ret = new VectorTexCoordGen();

			ret.vectorU = vectorU;
			ret.vectorV = vectorV;

			return ret;
		}

		public override int GetHashCode() { return vectorU.GetHashCode() ^ vectorV.GetHashCode(); }
		public override bool Equals( object obj )
		{
			VectorTexCoordGen other = obj as VectorTexCoordGen;
			if( other == null )
				return false;

			return other.vectorU == vectorU &&
				other.vectorV == vectorV;
		}
	}

	public abstract class TexCoordMod
	{
		internal TexCoordMod() { }

		public abstract TexCoordMod Clone();
	}

	/// <summary>
	/// Parallax Occlusion Mapping modifier.
	/// </summary>
	/// <remarks>
	/// This modifier is INVALID if used with any other
	/// texture coord modifiers or with any <see cref="TexCoordGen"/>
	/// other than <see cref="TextureTexCoordGen"/>.
	/// </remarks>
	public sealed class PomTexCoordMod : TexCoordMod
	{
		private string bumpTextureName = string.Empty;
		public string BumpTextureName { get { return bumpTextureName; } set { bumpTextureName = value ?? string.Empty; } }

		private float bumpDepth = 0.01F;
		public float BumpDepth { get { return bumpDepth; } set { bumpDepth = value; } }

		public override TexCoordMod Clone()
		{
			PomTexCoordMod ret = new PomTexCoordMod();

			ret.bumpTextureName = bumpTextureName;
			ret.bumpDepth = bumpDepth;

			return ret;
		}

		public override int GetHashCode()
		{
			return bumpTextureName.GetHashCode() + bumpDepth.GetHashCode();
		}

		public override bool Equals( object obj )
		{
			PomTexCoordMod other = obj as PomTexCoordMod;
			return other != null &&
				other.bumpTextureName == bumpTextureName &&
				other.bumpDepth == bumpDepth;
		}
	}

	public sealed class TurbTexCoordMod : TexCoordMod
	{
		private float _base;
		public float Base { get { return _base; } set { _base = value; } }

		private float amplitude;
		public float Amplitude { get { return amplitude; } set { amplitude = value; } }

		private float phase;
		public float Phase { get { return phase; } set { phase = value; } }

		private float frequency;
		public float Frequency { get { return frequency; } set { frequency = value; } }

		public override TexCoordMod Clone()
		{
			TurbTexCoordMod ret = new TurbTexCoordMod();

			ret._base = _base;
			ret.amplitude = amplitude;
			ret.phase = phase;
			ret.frequency = frequency;

			return ret;
		}

		public override int GetHashCode() { return _base.GetHashCode() + amplitude.GetHashCode() + phase.GetHashCode() + frequency.GetHashCode(); }
		public override bool Equals( object obj )
		{
			TurbTexCoordMod other = obj as TurbTexCoordMod;
			return other != null &&
				other._base == _base &&
				other.amplitude == amplitude &&
				other.phase == phase &&
				other.frequency == frequency;
		}
	}

	public sealed class ScaleTexCoordMod : TexCoordMod
	{
		private Vector2 scale;
		public Vector2 Scale { get { return scale; } set { scale = value; } }

		public override TexCoordMod Clone()
		{
			ScaleTexCoordMod ret = new ScaleTexCoordMod();

			ret.scale = scale;

			return ret;
		}

		public override int GetHashCode() { return scale.GetHashCode(); }
		public override bool Equals( object obj )
		{
			ScaleTexCoordMod other = obj as ScaleTexCoordMod;
			return other != null && other.scale == scale;
		}
	}

	public sealed class ScrollTexCoordMod : TexCoordMod
	{
		private Vector2 speed;
		public Vector2 Speed { get { return speed; } set { speed = value; } }

		public override TexCoordMod Clone()
		{
			ScrollTexCoordMod ret = new ScrollTexCoordMod();

			ret.speed = speed;

			return ret;
		}

		public override int GetHashCode() { return speed.GetHashCode(); }
		public override bool Equals( object obj )
		{
			ScrollTexCoordMod other = obj as ScrollTexCoordMod;
			return other != null && other.speed == speed;
		}
	}

	public sealed class RotateTexCoordMod : TexCoordMod
	{
		private float speed;
		public float Speed { get { return speed; } set { speed = value; } }

		public override TexCoordMod Clone()
		{
			RotateTexCoordMod ret = new RotateTexCoordMod();

			ret.speed = speed;

			return ret;
		}

		public override int GetHashCode() { return speed.GetHashCode(); }
		public override bool Equals( object obj )
		{
			RotateTexCoordMod other = obj as RotateTexCoordMod;
			return other != null && other.speed == speed;
		}
	}

	public sealed class StretchTexCoordMod : TexCoordMod
	{
		private Waveform waveform;
		public Waveform Waveform { get { return waveform; } set { waveform = value; } }

		public override TexCoordMod Clone()
		{
			StretchTexCoordMod ret = new StretchTexCoordMod();

			ret.waveform = waveform;

			return ret;
		}

		public override int GetHashCode() { return waveform.GetHashCode(); }
		public override bool Equals( object obj )
		{
			StretchTexCoordMod other = obj as StretchTexCoordMod;
			return other != null && other.waveform == waveform;
		}
	}

	public sealed class TransformTexCoordMod : TexCoordMod
	{
		private Matrix2x3 matrix = Matrix2x3.Identity;
		public Matrix2x3 Matrix { get { return matrix; } set { matrix = value; } }

		public override TexCoordMod Clone()
		{
			TransformTexCoordMod ret = new TransformTexCoordMod();

			ret.matrix = matrix;

			return ret;
		}

		public override int GetHashCode() { return matrix.GetHashCode(); }
		public override bool Equals( object obj )
		{
			TransformTexCoordMod other = obj as TransformTexCoordMod;
			return other != null && other.matrix == matrix;
		}
	}

	public class TextureBlend
	{
		private TextureBlendOp colorOp = TextureBlendOp.Modulate;
		public TextureBlendOp ColorOperation { get { return colorOp; } set { colorOp = value; } }

		private TextureBlendOp alphaOp = TextureBlendOp.Modulate;
		public TextureBlendOp AlphaOperation { get { return alphaOp; } set { alphaOp = value; } }

		private TextureBlendSource source0 = new TextureBlendSource( TextureBlendSourceSrc.Texture, TextureBlendSourceColorOp.Color, TextureBlendSourceSrc.Texture, TextureBlendSourceAlphaOp.Alpha );
		public TextureBlendSource Source0 { get { return source0; } }

		private TextureBlendSource source1 = new TextureBlendSource( TextureBlendSourceSrc.Previous, TextureBlendSourceColorOp.Color, TextureBlendSourceSrc.Previous, TextureBlendSourceAlphaOp.Alpha );
		public TextureBlendSource Source1 { get { return source1; } set { source1 = value; } }

		private TextureBlendSource source2 = new TextureBlendSource( TextureBlendSourceSrc.Constant, TextureBlendSourceColorOp.Alpha, TextureBlendSourceSrc.Constant, TextureBlendSourceAlphaOp.Alpha );
		public TextureBlendSource Source2 { get { return source1; } set { source1 = value; } }

		private float colorScale = 1;
		public float ColorScale { get { return colorScale; } set { colorScale = value; } }

		private float alphaScale = 1;
		public float AlphaScale { get { return alphaScale; } set { alphaScale = value; } }

		private Color constantColor = Color.Black;
		public Color ConstantColor { get { return constantColor; } set { constantColor = value; } }

		public TextureBlend Clone()
		{
			TextureBlend ret = new TextureBlend();

			ret.colorOp = colorOp;
			ret.alphaOp = alphaOp;
			ret.source0 = source0.Clone();
			ret.source1 = source1.Clone();
			ret.source2 = source2.Clone();
			ret.colorScale = 1;
			ret.alphaScale = 1;
			ret.constantColor = constantColor;

			return ret;
		}
	}

	public enum TextureBlendOp
	{
		/// <summary>
		/// Return Source0.
		/// </summary>
		Replace,
		/// <summary>
		/// Return Source0 + Source1.
		/// </summary>
		Add,
		/// <summary>
		/// Return Source0 - Source1.
		/// </summary>
		Subtract,
		/// <summary>
		/// Return Source0 * Source1.
		/// </summary>
		Modulate,
		/// <summary>
		/// Return Source0 * Source2 + Source1 * (1 - Source2).
		/// </summary>
		Interpolate,
	}

	public class TextureBlendSource
	{
		public TextureBlendSource()
		{
		}

		public TextureBlendSource( TextureBlendSourceSrc colorSource, TextureBlendSourceColorOp colorOperation,
			TextureBlendSourceSrc alphaSource, TextureBlendSourceAlphaOp alphaOperation )
		{
			colorSrc = colorSource;
			colorOp = colorOperation;
			alphaSrc = alphaSource;
			alphaOp = alphaOperation;
		}

		private TextureBlendSourceSrc colorSrc;
		public TextureBlendSourceSrc ColorSource { get { return colorSrc; } set { colorSrc = value; } }

		private TextureBlendSourceColorOp colorOp;
		public TextureBlendSourceColorOp ColorOperation { get { return colorOp; } set { colorOp = value; } }
		
		private TextureBlendSourceSrc alphaSrc;
		public TextureBlendSourceSrc AlphaSource { get { return alphaSrc; } set { alphaSrc = value; } }

		private TextureBlendSourceAlphaOp alphaOp;
		public TextureBlendSourceAlphaOp AlphaOperation { get { return alphaOp; } set { alphaOp = value; } }

		public TextureBlendSource Clone()
		{
			TextureBlendSource ret = new TextureBlendSource();

			ret.colorSrc = colorSrc;
			ret.colorOp = colorOp;
			ret.alphaSrc = alphaSrc;
			ret.alphaOp = alphaOp;

			return ret;
		}
	}

	public enum TextureBlendSourceSrc
	{
		Texture,
		Constant,
		Primary,
		Previous,
	}

	public enum TextureBlendSourceColorOp
	{
		Color,
		InverseColor,
		Alpha,
		InverseAlpha,
	}

	public enum TextureBlendSourceAlphaOp
	{
		Alpha,
		InverseAlpha,
	}

	public class SkyParams
	{
		private float cloudHeight;
		public float CloudHeight { get { return cloudHeight; } set { cloudHeight = value; } }

		private string image;
		public string Image { get { return image; } set { image = value; } }

		public SkyParams Clone()
		{
			SkyParams ret = new SkyParams();
			
			ret.cloudHeight = cloudHeight;
			ret.image = image;

			return ret;
		}
	}

	public enum WaveFunction
	{
		None,
		
		Sine,
		Square,
		Triangle,
		Sawtooth,
		InverseSawtooth,

		Noise,
	}

	public struct Waveform
	{
		private WaveFunction function;
		public WaveFunction Function { get { return function; } set { function = value; } }

		private float _base;
		public float Base { get { return _base; } set { _base = value; } }

		private float amplitude;
		public float Amplitude { get { return amplitude; } set { amplitude = value; } }

		private float phase;
		public float Phase { get { return phase; } set { phase = value; } }

		private float frequency;
		public float Frequency { get { return frequency; } set { frequency = value; } }

		public override int GetHashCode()
		{
			return _base.GetHashCode() ^ amplitude.GetHashCode() ^
				phase.GetHashCode() ^ frequency.GetHashCode();
		}

		public override bool Equals( object obj )
		{
			try
			{
				Waveform other = (Waveform)obj;

				return other._base == _base &&
					other.amplitude == amplitude &&
					other.phase == phase &&
					other.frequency == frequency;
			}
			catch( InvalidCastException )
			{
				return false;
			}
		}

		public static bool operator ==( Waveform a, Waveform b )
		{
			return a._base == b._base &&
				a.amplitude == b.amplitude &&
				a.phase == b.phase &&
				a.frequency == b.frequency;
		}

		public static bool operator !=( Waveform a, Waveform b )
		{
			return a._base != b._base ||
				a.amplitude != b.amplitude ||
				a.phase != b.phase ||
				a.frequency != b.frequency;
		}
	}
}